package org.tool.datasource;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.tool.io.IOUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.text.CharSequenceUtil.SPACE;
import static org.common.constant.MagicValue.*;

/**
 * 表结构解析相关工具类
 *
 * @author 李坤
 * @date 2022/6/30 23:08
 */
public abstract class TableStructureParseUtil {

    /**
     * MySQL注释前缀
     */
    private static final String NOTE = " comment ";

    /**
     * 建表语句的前缀
     */
    public static final String CREATE_TABLE_PREFIX = "create table ";

    /**
     * MySQL字段类型关键字
     */
    private static final List<String> MYSQL_TABLE_FIELD_KEYWORDS =
            Arrays.asList(" int", " date", " char", " text", " decimal", " numeric", " datetime", " varchar", " timestamp", " longtext");


    /*----------------------------------------------public static methods---------------------------------------------*/


    /**
     * 读取给定的流中的数据，并将其解析为表结构数据（MySQL）
     *
     * @param inputStream 输入流
     *
     * @return 表结构定义
     *
     * @throws IOException 读取流失败
     */
    public static List<TableStructure> parseTableStructure4MySQL(InputStream inputStream) throws IOException {
        return parseTableStructure4MySQL(IOUtil.readStrUtf8(inputStream));
    }

    /**
     * 将给定的字符串文本解析为表结构数据（MySQL）
     *
     * @param text 字符串文本
     *
     * @return 表结构定义
     */
    public static List<TableStructure> parseTableStructure4MySQL(String text) {
        return StrUtil.isEmpty(text) ? Collections.emptyList() : split2TableText(text).stream().map(TableStructure::getInstance).collect(Collectors.toList());
    }


    /*----------------------------------------------public static methods---------------------------------------------*/


    /**
     * 将原始的字符串文本根据表结构定义进行切分
     *
     * @param text 原始的文本
     *
     * @return 文本（表结构文本）集合
     */
    private static List<String> split2TableText(String text) {
        // 替换换行符，并将字符转换为小写
        final String[] strings = text.replace(ENTRY_NEWLINE, SPACE).replace(NEWLINE_STR, SPACE).toLowerCase().split(CREATE_TABLE_PREFIX);
        // 将原始字符串根据表结构分割
        final List<String> textList = Arrays.stream(strings).map(String::trim).collect(Collectors.toList());
        // 去除可能存在的前缀
        return textList.subList(1, textList.size());
    }


    /*-----------------------------------------------public static class----------------------------------------------*/


    @Data
    @NoArgsConstructor
    public static class TableStructure implements Serializable {

        /**
         * 表名
         */
        private String tableName;

        /**
         * 表-字段明细
         */
        private List<TableFieldDetail> tableFieldDetails;

        public TableStructure(String tableName, List<TableFieldDetail> tableFieldDetails) {
            if (tableName.startsWith("ifnotexists")) {
                tableName = tableName.substring("ifnotexists".length());
            }
            this.tableName = tableName;
            this.tableFieldDetails = tableFieldDetails;
        }

        /**
         * 根据原始的文本数据获取表结构的定义数据信息
         *
         * @param originalStatement 原始文本数据（需要去除空格）
         *
         * @return 表结构定义
         */
        public static TableStructure getInstance(String originalStatement) {
            if (StrUtil.isNotBlank(originalStatement)) {
                // 栈-判断表结构定义范围
                final Stack<Character> stack = new Stack<>();
                // 表结构-字段定义文本集合
                final List<String> tableFieldTextList = new LinkedList<>();

                // 字符串构建器-处理单个字段明细
                StringBuilder builder4TableField = new StringBuilder();
                // 字符串构建器-处理表名
                final StringBuilder builder4TableName = new StringBuilder();

                for (char value : originalStatement.toCharArray()) {
                    // 当第一个左括号开始进栈，表明之后的字符为字段明细，当栈再次为空，则停止处理剩余的字符
                    if (value == LEFT_PARENTHESIS_CHAR) {
                        stack.push(value);
                    } else if (value == RIGHT_PARENTHESIS_CHAR) {
                        stack.pop();
                        if (stack.empty()) {
                            tableFieldTextList.add(builder4TableField.toString().trim());
                            break;
                        }
                    }

                    if (stack.empty()) {
                        if (value != SPACE_CHAR) {
                            builder4TableName.append(value);
                        }
                    } else {
                        // 字段间的定义以 ',' 进行分隔
                        if (value == DELIMITER_CHAR && stack.size() == 1) {
                            tableFieldTextList.add(builder4TableField.toString().trim());
                            builder4TableField = new StringBuilder();
                        } else if (!(stack.size() == 1 && value == LEFT_PARENTHESIS_CHAR)) {
                            builder4TableField.append(value);
                        }
                    }
                }
                return new TableStructure(builder4TableName.toString(), tableFieldTextList.stream()
                        .map(text -> text.split(NOTE)[0].trim()).map(TableFieldDetail::getInstance)
                        .filter(Objects::nonNull).collect(Collectors.toList()));
            }
            return null;
        }

        /**
         * 比较两个表结构的字段是否相等
         *
         * @param tableStructurePDM PDM表结构
         */
        public void compareTo(TableStructure tableStructurePDM) {
            System.out.println("---------------------------------------" + this.tableName + ": " + this.tableFieldDetails.size() + "---------------------------------------");

            boolean status = true;
            List<TableFieldDetail> tableFieldDetailsPDM = tableStructurePDM.tableFieldDetails;

            System.out.println("PDM 字段：" + tableFieldDetailsPDM);
            System.out.println("数据库 字段：" + this.tableFieldDetails);

            // 判断 pdm 中是否存在 database 中不存在字段
            final List<TableFieldDetail> extraFields = tableFieldDetailsPDM.stream()
                    .filter(field -> !this.tableFieldDetails.contains(field)).collect(Collectors.toList());

            if (ObjectUtil.isNotEmpty(extraFields)) {
                status = false;
                System.out.println("pdm中多余的字段" + extraFields);
            }

            // 判断 pdm 中是否缺少 database 中的字段
            final List<TableFieldDetail> lackFields = tableFieldDetails.stream()
                    .filter(field -> !tableFieldDetailsPDM.contains(field)).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(lackFields)) {
                status = false;
                System.out.println("pdm中缺少的字段" + lackFields);
            }
            System.out.println("判断结果：" + status);
        }

    }

    @Data
    @NoArgsConstructor
    public static class TableFieldDetail implements Serializable {

        /**
         * 字段名称
         */
        private String field;

        /**
         * 字段类型
         */
        private String fieldType;

        public TableFieldDetail(String field, String fieldType) {
            this.field = field;

            // 处理 numeric 与 decimal 间的映射关系
            if (fieldType.contains("numeric")) {
                fieldType = fieldType.replace("numeric", "decimal");
            }
            if (fieldType.contains("decimal") && fieldType.contains(",0")) {
                final String[] split = fieldType.split(",0");
                fieldType = split[0] + split[1];
            }
            this.fieldType = fieldType;
        }

        @Override
        public int hashCode() {
            return Objects.hash(field, fieldType);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            TableFieldDetail that = (TableFieldDetail) o;
            return field.equals(that.field) && fieldType.equals(that.fieldType);
        }

        @Override
        public String toString() {
            return "{" + field + '\'' + ", '" + fieldType + '\'' + '}';
        }

        /**
         * 根据语句创建 {@link TableFieldDetail} 对象
         *
         * @param text 建表语句（需要去除空格）
         *
         * @return {@link TableFieldDetail} 对象
         */
        public static TableFieldDetail getInstance(String text) {
            if (StrUtil.isNotBlank(text)) {
                text = text.trim();

                int keywordIndex;
                final Stack<Character> stack = new Stack<>();

                for (String keyword : MYSQL_TABLE_FIELD_KEYWORDS) {
                    int indexOf = 0;

                    if ((keywordIndex = text.indexOf(keyword, indexOf)) != -1) {

                        final char charAt;
                        // 校验关键字
                        final int checkCharIndex = keywordIndex + keyword.length();
                        if ((text.length() == checkCharIndex) || (charAt = text.charAt(checkCharIndex)) == SPACE_CHAR || charAt == LEFT_PARENTHESIS_CHAR) {
                            char value;
                            final char[] chars = text.toCharArray();

                            // 是否构建字段名称
                            boolean appendTableField = true;
                            // 是否构建字段类型
                            boolean openingCheckBracket = false;
                            // 构造器-字段
                            final StringBuilder builder4Field = new StringBuilder();
                            // 构造器-字段类型
                            final StringBuilder builder4FieldType = new StringBuilder();

                            for (int index = 0; index < chars.length; index++) {
                                value = chars[index];
                                if (index <= keywordIndex) {
                                    if (value == SPACE_CHAR) {
                                        appendTableField = false;
                                        index = keywordIndex;
                                        continue;
                                    }
                                    builder4Field.append(value);
                                } else {
                                    if (value == SPACE_CHAR) {
                                        if (stack.empty()) {
                                            openingCheckBracket = true;
                                        }
                                    } else if (!openingCheckBracket) {
                                        builder4FieldType.append(value);
                                        if (value == LEFT_PARENTHESIS_CHAR) {
                                            stack.push(value);
                                        } else if (value == RIGHT_PARENTHESIS_CHAR) {
                                            if (stack.size() == 1) {
                                                break;
                                            }
                                        }
                                    } else {
                                        // 校验字符
                                        if (value == LEFT_PARENTHESIS_CHAR) {
                                            stack.push(value);
                                            builder4FieldType.append(value);
                                        } else if (!stack.empty()) {
                                            builder4FieldType.append(value);
                                            if (value == RIGHT_PARENTHESIS_CHAR) {
                                                stack.pop();
                                            }
                                        }
                                        if (stack.empty()) {
                                            break;
                                        }
                                    }
                                }
                            }
                            return new TableFieldDetail(builder4Field.toString(), builder4FieldType.toString());
                        }
                    }
                }
            }
            return null;
        }

    }

}
